package dev.ece.suit.hexmap.terrain;

import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.RandomUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL11;

import dev.ece.core.light.Material;
import dev.ece.core.texture.Texture;
import dev.ece.suit.hexmap.MapCellCreator;
import dev.ece.suit.hexmap.MapCell;
import dev.ece.suit.hexmap.MapCrevice;
import dev.ece.suit.hexmap.MapCreviceCreator;
import dev.ece.suit.hexmap.MapTriangle;
import dev.ece.suit.hexmap.MapTriangleCreator;
import dev.ece.suit.hexmap.MapWater;
import dev.ece.suit.hexmap.Terrain;
import dev.ece.suit.hexmap.TerrainModelCreator;
import dev.ece.util.lib.Vector2f;
import dev.ece.util.lib.Vector3f;
import dev.ece.util.tools.Constants;
import dev.ece.util.tools.Direction;
import dev.ece.util.tools.ploy.TextureMipmapPloy;

public class SimpleTerrainModelCreator implements TerrainModelCreator {

	private static final Logger LOG = LogManager.getLogger(SimpleTerrainModelCreator.class);
	
	private Terrain[] terrains;
	
	private MapCellCreator mapCellCreator;
	
	private Texture noise;
	
	private Texture[] textures;
	
	private Material waterMaterial;
	
	private MapCreviceCreator mapCreviceCreator;
	
	private MapTriangleCreator mapTriangleCreator;
	
	public SimpleTerrainModelCreator(Terrain[] terrains, float cellOutSize, int circles, Texture[] textures) {
		this(terrains, cellOutSize, circles, textures, new Material());
		setDefaultMaterial();
	}
	
	public SimpleTerrainModelCreator(Terrain[] terrains, float cellOutSize, int circles, String[] textures) {
		this(terrains, cellOutSize, circles, textures, new Material());
		setDefaultMaterial();
	}
	
	public SimpleTerrainModelCreator(Terrain[] terrains, float cellOutSize, int circles, BufferedImage[] textures) {
		this(terrains, cellOutSize, circles, textures, new Material());
		setDefaultMaterial();
	}
	
	public SimpleTerrainModelCreator(Terrain[] terrains, float cellOutSize, int circles, Texture[] textures, Material waterMaterial) {
		this.terrains = terrains;
		this.waterMaterial = waterMaterial;
		this.textures = textures;
		this.noise = createWaterNoiseTexture();
		this.mapCellCreator = createMapCellCreator(cellOutSize, circles);
		this.mapCreviceCreator = createMapCreviceCreator();
		this.mapTriangleCreator = createMapTriangleCreator();
	}
	
	public SimpleTerrainModelCreator(Terrain[] terrains, float cellOutSize, int circles, BufferedImage[] textures, Material waterMaterial) {
		this.terrains = terrains;
		this.waterMaterial = waterMaterial;
		this.textures = createTextures(textures);
		this.noise = createWaterNoiseTexture();
		this.mapCellCreator = createMapCellCreator(cellOutSize, circles);
		this.mapCreviceCreator = createMapCreviceCreator();
		this.mapTriangleCreator = createMapTriangleCreator();
	}
	
	public SimpleTerrainModelCreator(Terrain[] terrains, float cellOutSize, int circles, String[] textures, Material waterMaterial) {
		this.terrains = terrains;
		this.waterMaterial = waterMaterial;
		this.textures = createTextures(textures);
		this.noise = createWaterNoiseTexture();
		this.mapCellCreator = createMapCellCreator(cellOutSize, circles);
		this.mapCreviceCreator = createMapCreviceCreator();
		this.mapTriangleCreator = createMapTriangleCreator();
	}
	
	protected void setDefaultMaterial() {
		waterMaterial.getDiffuse().set(0.8f, 0.8f, 0.8f);
		waterMaterial.getSpecular().set(0.6f, 0.6f, 0.6f);
		waterMaterial.setShininess(8f);
		waterMaterial.setAlpha(0.6f);
	}
	
	protected MapTriangleCreator createMapTriangleCreator() {
		return new SimpleMapTriangleCreator(this);
	}
	
	protected MapCellCreator createMapCellCreator(float cellOutSize, int circles) {
		return new SimpleCellCreator(this, cellOutSize, circles);
	}
	
	protected MapCreviceCreator createMapCreviceCreator() {
		return new SimpleMapCreviceCreator(this);
	}
	
	protected Texture[] createTextures(BufferedImage[] textures) {
		int len = textures.length;
		Texture[] result = new Texture[len];
		for(int i=0; i<len; i++) {
			result[i] = Texture.newInstance(textures[i], TextureMipmapPloy.getInstance());
		}
		return result;
	}
	
	protected Texture[] createTextures(String[] textures) {
		int len = textures.length;
		Texture[] result = new Texture[len];
		for(int i=0; i<len; i++) {
			try {
				result[i] = Texture.newInstance(Constants.getGameFileInputStream(textures[i]), TextureMipmapPloy.getInstance());
			} catch (FileNotFoundException e) {
				LOG.error("无法找到地形纹理文件", e);
			}
		}
		return result;
	}
	
	protected Texture createWaterNoiseTexture() {
		return Texture.newInstance(
				SimpleTerrainModelCreator.class.getClassLoader().getResourceAsStream("images/noise.png"), 
				TextureMipmapPloy.getInstance());
	}
	
	@Override
	public float getCellOutSize() {
		return getMapCellCreator().getCellOutSize();
	}

	@Override
	public float getCellInSize() {
		return getMapCellCreator().getCellInSize();
	}

	@Override
	public void render(MapCell cell) {
		GL11.glDrawArrays(GL11.GL_TRIANGLES, cell.getFirst(), getMapCellCreator().count());
	}

	@Override
	public void render(MapCrevice crevice) {
		GL11.glDrawArrays(GL11.GL_TRIANGLES, crevice.getFirst(), getMapCreviceCreator().count());
	}

	@Override
	public void render(MapTriangle triangle) {
		// TODO Auto-generated method stub

	}

	@Override
	public void render(MapWater water) {
		GL11.glDrawArrays(GL11.GL_TRIANGLE_FAN, water.getFirst(), 8);
	}

	@Override
	public Texture[] getTextures() {
		return this.textures;
	}

	@Override
	public Texture getNoise() {
		return this.noise;
	}

	@Override
	public Material getWaterMaterial() {
		return this.waterMaterial;
	}

	@Override
	public MapCell[][] createMapCells(int[][] terrains) {
		MapCell[][] result = new MapCell[terrains.length][];
		int[] cols = null;
		int index = 0;
		for(int row=0; row<terrains.length; row ++) {
			cols = terrains[row];
			result[row] = new MapCell[cols.length];
			for(int col=0; col<cols.length; col ++) {
				result[row][col] = getMapCellCreator().createMapCell(cols[col], index ++, row, col);
				translate(result[row][col]);
			}
		}
		return result;
	}

	protected void translate(MapCell cell) {
		int col = cell.getCol();
		int row = cell.getRow();
		Vector3f position = new Vector3f(
				getCellOutSize() * 2 * col + (row & 1) * getCellOutSize(), 
				0, 
				(float) (2 * row * getCellOutSize() * Math.sin(Math.toRadians(60))));
		Vector2f offset = new Vector2f(
				0.5f * (row & 1) + 1.0f * col,
				(float) Math.sin(Math.toRadians(60)) * row);
		float distance = getCellOutSize() / getMapCellCreator().getCircles() / 3;
		float hdistance = distance / 2;
		cell.getCenter().add(position);
		cell.getCenter().add(createIrregularization(distance, hdistance));
		cell.getCenteruv().add(offset);
		cell.getCenteruv().mul(getCellOutSize());
		Vector3f[] vertice = null;
		Vector2f[] uv = null;
		for(int i = 0; i<cell.getVertice().length; i ++) {
			vertice = cell.getVertice()[i];
			uv = cell.getUv()[i];
			for(int j=0; j<vertice.length; j++) {
				vertice[j].add(position);
				vertice[j].add(createIrregularization(distance, hdistance));
				uv[j].add(offset);
				uv[j].mul(getCellOutSize());
			}
		}
	}
	
	protected Vector3f createIrregularization(float distance, float hdistance) {
		return new Vector3f(
				RandomUtils.nextFloat(0, distance) - hdistance, 
				0, 
				RandomUtils.nextFloat(0, distance) - hdistance);
	}
	
	@Override
	public Terrain[] getTerrains() {
		return terrains;
	}

	@Override
	public MapCellCreator getMapCellCreator() {
		return mapCellCreator;
	}

	@Override
	public MapTriangleCreator getMapTriangleCreator() {
		return mapTriangleCreator;
	}

	@Override
	public MapCrevice[] createMapCrevices(MapCell[][] cells) {
		List<MapCrevice> result = new ArrayList<MapCrevice>();
		MapCell[] cols = null;
		MapCell left = null;
		MapCell right = null;
		int prow = 0;
		int nrow = 0;
		int ncol = 0;
		int index = 0;
		for(int row=0; row<cells.length; row++) {
			cols = cells[row];
			prow = row - 1;
			nrow = row + 1;
			for(int col=0; col<cols.length; col++) {
				left = cells[row][col];
				ncol = col + 1;
				if((row & 1) == 0) {
					//偶数行
					if(prow > -1) {
						//右上
						right = cells[prow][col];
						//创建一个
						result.add(getMapCreviceCreator().create(index ++, left, right, 5));
					}
					if(nrow < cells.length) {
						//右下
						right = cells[nrow][col];
						//创建一个
						result.add(getMapCreviceCreator().create(index ++, left, right, 3));
					}
				} else {
					//奇数行
					if(ncol < cols.length) {
						if(prow > -1) {
							//右上
							right = cells[prow][ncol];
							//创建一个
							result.add(getMapCreviceCreator().create(index ++, left, right, 5));
						}
						if(nrow < cells.length) {
							//右下
							right = cells[nrow][ncol];
							//创建一个
							result.add(getMapCreviceCreator().create(index ++, left, right, 3));
						}
					}
				}
				if(ncol < cols.length) {
					//右
					right = cells[row][ncol];
					//创建一个
					result.add(getMapCreviceCreator().create(index ++, left, right, 4));
				}
			}
		}
		return result.toArray(new MapCrevice[] {});
	}
	
	@Override
	public MapTriangle[] createMapTriangles(MapCell[][] cells) {
		List<MapTriangle> result = new ArrayList<MapTriangle>();
		MapCell[] cols = null;
		int ncol = 0;
		int prow = 0;
		int nrow = 0;
		int index = 0;
		for(int row=0; row<cells.length; row++) {
			cols = cells[row];
			for(int col=0; col<cols.length; col++) {
				ncol = col + 1;
				if(ncol < cols.length) {
					if((row & 1) == 0) {
						//偶数行
						if(prow > -1) {
							//上
							result.add(getMapTriangleCreator().create(index ++, cols[col], cells[prow][col], cols[ncol], Direction.DIRECTION_0));
						}
						if(nrow < cells.length) {
							//下
							result.add(getMapTriangleCreator().create(index ++, cols[col], cells[nrow][col], cols[ncol], Direction.DIRECTION_1));
						}
					} else {
						//奇数行
						if(prow > -1) {
							//上
							result.add(getMapTriangleCreator().create(index ++, cols[col], cells[prow][ncol], cols[ncol], Direction.DIRECTION_0));
						}
						if(nrow < cells.length) {
							//下
							result.add(getMapTriangleCreator().create(index ++, cols[col], cells[nrow][ncol], cols[ncol], Direction.DIRECTION_1));
						}
					}
				}
			}
		}
		return result.toArray(new MapTriangle[] {});
	}

	@Override
	public MapWater[] createMapWaters(MapCell[][] cells) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public MapCreviceCreator getMapCreviceCreator() {
		return mapCreviceCreator;
	}

	@Override
	public void destroy() {
		if(getTextures() != null) {
			for(Texture texture: getTextures()) {
				texture.destroy();
			}
			this.textures = null;
		}
		if(getNoise() != null) {
			getNoise().destroy();
			this.noise = null;
		}
		this.waterMaterial = null;
		this.terrains = null;
		this.mapCellCreator = null;
		this.mapCreviceCreator = null;
		this.mapTriangleCreator = null;
	}

}

